#include "View.h"

View::View(void)
{
}

View::~View(void)
{
}

View::View(const View &v)
{
}

View* View::operator=(const View &v)
{
	return this;
}

View* View::Instance()
{
	static View instance;

	return &instance;
}

// Initializes the FX file among other things
void View::InitializeFX(void)
{
	ID3DXBuffer* errors = 0;
	// load in the Effect file and check for errors
	D3DXCreateEffectFromFile(gD3DDevice, "Effects/dirLightTex.fx", 0, 0, D3DXSHADER_DEBUG, 0, &mFX, &errors);
	if (errors)
		MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);

	// Obtain handles from the Effect file
	mhTech           = mFX->GetTechniqueByName("DirLightTexTech");
	mhWVP            = mFX->GetParameterByName(0, "gWVP");
	mhWorldInvTrans  = mFX->GetParameterByName(0, "gWorldInvTrans");
	mhEyePos         = mFX->GetParameterByName(0, "gEyePosW");
	mhWorld          = mFX->GetParameterByName(0, "gWorld");
	mhAmbientLight   = mFX->GetParameterByName(0, "gAmbientLight");
	mhDiffuseLight   = mFX->GetParameterByName(0, "gDiffuseLight");
	mhSpecLight      = mFX->GetParameterByName(0, "gSpecularLight");
	mhLightVecW      = mFX->GetParameterByName(0, "gLightVecW");
	mhAmbientMtrl    = mFX->GetParameterByName(0, "gAmbientMtrl");
	mhDiffuseMtrl    = mFX->GetParameterByName(0, "gDiffuseMtrl");
	mhSpecMtrl       = mFX->GetParameterByName(0, "gSpecularMtrl");
	mhSpecPower      = mFX->GetParameterByName(0, "gSpecularPower");
	mhTex            = mFX->GetParameterByName(0, "gTex");

	mSky = new Sky("Assets//Textures//space.dds", 100.0f);
}

// Initializes the Projection matrix. Should 
// be called each time the backbuffer changes
void View::InitializeProjectionMatrix(int w, int h)
{
	D3DXMatrixPerspectiveFovLH(&mProj, D3DX_PI * 0.25f, (float)(w/h), 1.0f, 5000.0f);
}

// initializes the lighting effect
void View::InitializeLighting(void)
{
	// initialize the colours of the ambient, diffise and specular lights
	mAmbientLight		= WHITE*0.1f;
	mDiffuseLight		= WHITE;
	mSpecLight			= WHITE;

	// initialize the light position
	mLightVecW			= D3DXVECTOR3(0.7f, 1.0f, 0.7f);

	// set the attenuation of the specular light (the behaviour of the light as it fades from the central point of impact)
	mAttenuation012		= D3DXVECTOR3(0.5f, 0.0f, 0.0f);
}

// begin the DX scene
void View::BeginScene(void)
{
	HR(gD3DDevice->BeginScene());
	HR(gD3DDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000000, 1.0f, 0));
}

// renders the object to the screen based on it's x/y position
void View::RenderObject(ObjectType type, int x, int y)
{
	// convert the object's Coordinate information into a D3DXMATRIX transform
	// then call the main render object function
	D3DXMATRIX transform, scale;
	D3DXVECTOR3 t = ConvertCoordinateToD3DXVector3(x, y);
	D3DXMatrixTranslation(&transform, t.x, t.y, t.z);
	D3DXMatrixScaling(&scale, OBJECT_SCALE, OBJECT_SCALE, OBJECT_SCALE);

	RenderObject(type, scale*transform);
}

// renders a UI element with the LRS information on it
void View::RenderLRSInfo(LRSInformation info, int x, int y, ID3DXFont* font)
{
	if (info.numSpaceStations > 0)
	{
		VIEW->RenderObject(OBJECTTYPE_SPACESTATION_GALAXY, x, y);
	}

	if (info.numStars == LRSINFO_UNKNOWN)
	{
		VIEW->RenderObject(OBJECTTYPE_QUESTIONMARK, x, y);
	}
	else if (info.numStars > 0)
	{
		RenderObject(OBJECTTYPE_STAR_GALAXY, x, y);
		static char buffer[TEXT_BUFFERSIZE];
		sprintf_s(buffer, "%i", info.numStars);
		
		TEXTMANAGER->DrawInstantText(font, true, buffer, TEXTSCALE_LRS, ConvertCoordinateToD3DXVector3(x, y, NUMSTARS_Y, NUMSTARS_XSCALE, NUMSTARS_YSCALE), TEXTCOLOR_NUMSTARS);
	}

	if (info.numEnemies > 0)
	{
		RenderObject(OBJECTTYPE_ENEMYSHIP_GALAXY, x, y);
		static char buffer[TEXT_BUFFERSIZE];
		sprintf_s(buffer, "%i", info.numEnemies);
		TEXTMANAGER->DrawInstantText(font, true, buffer, TEXTSCALE_LRS, ConvertCoordinateToD3DXVector3(x, y, NUMENEMIES_Y, NUMENEMIES_XSCALE, NUMENEMIES_YSCALE), TEXTCOLOR_NUMENEMIES);
	}
}

void View::RenderObject(ObjectType type, Coordinate c)
{
	RenderObject(type, c.x, c.y);
}

// renders the object to the screen
void View::RenderObject(ObjectType type, D3DXMATRIX transform)
{
	// get the data from the meshmanager
	MeshData* md = MESHMANAGER->GetMesh(type);

	// set the render state
	gD3DDevice->SetRenderState(D3DRS_WRAP0, D3DWRAP_U);

	D3DXMATRIX WIT;

	D3DXMatrixInverse(&WIT, 0, &transform);
	D3DXMatrixTranspose(&WIT, &WIT);

	// Setup the rendering FX
	mFX->SetValue(mhAmbientLight, &mAmbientLight, sizeof(D3DXCOLOR));
	mFX->SetValue(mhDiffuseLight, &mDiffuseLight, sizeof(D3DXCOLOR));
	mFX->SetValue(mhSpecLight, &mSpecLight, sizeof(D3DXCOLOR));
	mFX->SetValue(mhLightVecW, &mLightVecW, sizeof(D3DXVECTOR3));

	mFX->SetMatrix(mhWVP, &(transform * mView * mProj));
	mFX->SetMatrix(mhWorld, &transform);
	mFX->SetMatrix(mhWorldInvTrans, &WIT);

	// do the relevant number of passes through the Effect file
	UINT numPasses = 0;
	mFX->Begin(&numPasses, 0); // get the number of passes from the effect file
	for(UINT i = 0; i < numPasses; ++i) 
	{
		// begin the pass
		mFX->BeginPass(i);
		
		for (UINT j = 0; j < md->materialList->size(); ++j)
		{
			mFX->SetValue(mhAmbientMtrl, &md->materialList->operator[](j).ambient, sizeof(Mtrl));
			mFX->SetValue(mhDiffuseMtrl, &md->materialList->operator[](j).diffuse, sizeof(Mtrl));
			mFX->SetValue(mhSpecMtrl, &md->materialList->operator[](j).spec, sizeof(Mtrl));
			mFX->SetValue(mhSpecPower, &md->materialList->operator[](j).specPower, sizeof(Mtrl));
		
			if (md->textureList->operator[](j) != 0)
				mFX->SetTexture(mhTex, md->textureList->operator[](j));

			mFX->CommitChanges();

			md->mesh->DrawSubset(j);
		}

		// close the pass
		mFX->EndPass();
	}
	// close the entire Effect
	mFX->End();

	// reset the render state to default
	gD3DDevice->SetRenderState(D3DRS_WRAP0, 0);
}

// ends the DX scene
void View::EndScene(void)
{
	HR(gD3DDevice->EndScene());

	// Present the backbuffer.
	HR(gD3DDevice->Present(0, 0, 0, 0));
}

// updates the new view and proj matricies
void View::UpdateView(D3DXVECTOR3 pos, D3DXVECTOR3 target, D3DXVECTOR3 up)
{
	// updates the View's view and projection matrices, based
	// on the camera position and screen size
	D3DXMatrixLookAtLH(&mView, &pos, &target, &up);
	mCameraPos = pos;
	mFX->SetValue(mhEyePos, &pos, sizeof(D3DXVECTOR3));
}

// releases all memory assets
void View::ClearAll(void)
{
	mFX->Release();
	MESHMANAGER->ClearAll();
	delete mSky;
}

// performs necessary functions when a device is lost
void View::OnLostDevice(void)
{
	mFX->OnLostDevice();
	mSky->onLostDevice();
}

// performs necessary functions when a device is reset
void View::OnResetDevice(void)
{
	mFX->OnResetDevice();
	mSky->onResetDevice();
}

// gets the projection matrix
D3DXMATRIX View::GetProjectionMatrix(void)
{
	return mProj;
}

// gets the view matrix
D3DXMATRIX View::GetViewMatrix(void)
{
	return mView;
}

// gets the camera position
D3DXVECTOR3 View::GetCameraPos()
{
	return mCameraPos;
}

void View::RenderSkyBox()
{
	mSky->draw();
}

D3DXVECTOR3 ConvertCoordinateToD3DXVector3(Coordinate c, int yPosition, float xScaleMod, float yScaleMod)
{
	return ConvertCoordinateToD3DXVector3(c.x, c.y, yPosition, xScaleMod, yScaleMod);
}

D3DXVECTOR3 ConvertCoordinateToD3DXVector3(int x, int y, int yPosition, float xScaleMod, float yScaleMod)
{
	return D3DXVECTOR3((float)((x+0.5+xScaleMod) * WORLD_X_SCALE), yPosition, (float)((y+0.5+yScaleMod) * WORLD_Y_SCALE * -1));
}